package serveur;

import interfaces.*;
import java.rmi.*; 
import java.util.*; 
import java.rmi.server.UnicastRemoteObject;

public class ServeurImpl extends UnicastRemoteObject implements ServeurITF {

	Vector clients = new Vector();
	Vector banned = new Vector();
	int id = 0;

	boolean server_on = true;
	boolean is_used = false;

	public ServeurImpl() throws RemoteException {}
	
	public boolean connect(ClientITF client) throws RemoteException {
		
		boolean time_flag;
		boolean connect_flag;
		long timer;
		
		// Si le server accepte les connexions
		connect_flag = server_on && !isBanned(client);
		if (connect_flag)
		{
			// Tant que la connexion est utilisée
			while (is_used)
			{
				// On provoque un timeout de 5s	
				timer = System.currentTimeMillis();
				time_flag = false;
			
				while (!time_flag)
				{
					time_flag = (System.currentTimeMillis() - timer > 5000);
					try { Thread.sleep(200); } catch (Exception e) {}
				}		
			}
		
			// La connexion est maintenant possible, on se "connecte"
			is_used = true;
		
			System.out.println("* Connexion du client n°" + id);
			// On attribue une capacité au client
			client.setID(id++);
		
			// Le serveur ajoute le client de sa liste
			clients.addElement(client);
		
			// On donne au client ses voisins attribués
			Vector neighbours = neighbourhood(client);
			client.setNeighbourhood(neighbours);
			
			// On prévient les voisins du nouvel arrivant		
			for (ClientITF neighbour : (Vector<ClientITF>) neighbours)
			{
				neighbour.addNeighbour(client);
			}
			
			// Affichage du graphe
			System.out.println("Graphe :\n" + printGraph());
			
			// Fin de la zone critique		
			is_used = false;
		}

		return (connect_flag);
	}

	public void disconnect(ClientITF client) throws RemoteException {

		// Le serveur retire le client de sa liste
		clients.remove(client);
		
		// On prévient les voisins du client du départ
		// pour qu'ils l'enlèvent de leur liste de voisins
		Vector neighbours = client.getNeighbourhood();
		for (ClientITF neighbour : (Vector<ClientITF>) neighbours)
		{
			neighbour.removeNeighbour(client);
		}
	}
	
	public int nbClients() throws RemoteException
	{
		return clients.size();
	}
	
	public String printGraph() throws RemoteException
	{
		String r = "";
		
		int i = 0, n = clients.size();
		for (ClientITF c : (Vector<ClientITF>) clients)
		{
			r += c.getID() + " -> " + c.printNeighbours();
			
			if (i != n-1)
			{
				r += "\n";
			}
			
			i++;	
		}
		
		return r;
	}

	public Vector neighbourhood(ClientITF client) throws RemoteException {
		
		Random generator  = new Random(System.currentTimeMillis());
		Vector neighbours = new Vector();
		
		int nbNeighbours = Math.min(clients.size()-1, 2);
		int n1, n2;
		
		// Si on doit choisir 1 ou 2 voisins
		if (nbNeighbours >= 1)
		{
			// On choisit un premier voisin
			// On vérifie qu'on ne s'est pas choisi soi-même
			do
			{
				n1 = generator.nextInt(clients.size()-1);
			}
			while (clients.get(n1) == client);
			
			// On l'ajoute à liste des voisins à donner
			neighbours.add(clients.get(n1));
		
			// On choisit un deuxième voisin si nécessaire
			if (nbNeighbours == 2)
			{
				// On vérifie qu'on ne s'est pas choisi soi-même
				// ou le voisin choisi avant
				do
				{
					n2 = generator.nextInt(clients.size()-1);
				}
				while (clients.get(n2) == client || n1 == n2);
				
				// On l'ajoute à la liste des voisins à donner
				neighbours.add(clients.get(n2));
			}
		}
		
		return neighbours;
	}
	
	public boolean isBanned(ClientITF client) throws RemoteException
	{
		return banned.contains(client);
	}

	public void addToBanList(ClientITF client) throws RemoteException
	{
		if(!isBanned(client))
		{
			banned.addElement(client);
		}
	}

	public void removeFromBanList(ClientITF client) throws RemoteException
	{
		if(isBanned(client))
		{
			banned.remove(client);
		}
	}

	public void resultatElection(int idElu, ClientITF sender) throws RemoteException
	{
		// TODO : contacter le client a l'index idElu dans le vector des
		// clients pour lui signaler qu'il a été élu dans l'election
		// lancée par idSender
		ClientITF elu = (ClientITF)clients.elementAt(idElu);
		elu.choisiPar(sender);
	}

	public void switchOn() throws RemoteException
	{
		server_on = true;
	}
	
	public void switchOff() throws RemoteException
	{
		server_on = false;
	}
}
